#include "StdAfx.h"
FrameBuffer::FrameBuffer(FrameBufferDesc &desc):Base(desc)
{
	Width=desc.Width;
	Height=desc.Height;
	nColorAttachments=desc.nColorAttachments;
	bDepthEnabled=desc.bDepthEnabled;
	bStencilEnabled=desc.bStencilEnabled;
	Init();
}
FrameBuffer::~FrameBuffer(void)
{
	glDeleteFramebuffers(1, &FBOID);
	glDeleteRenderbuffers(1, &RBOID);
}
void FrameBuffer::Init()
{
	glGenFramebuffers(1, &FBOID);
	Bind();
	if(nColorAttachments>0)
		AttachColor(nColorAttachments);
	if(bDepthEnabled)
		AttachDepth();
	TestFBO();
	Unbind();
}
bool FrameBuffer::TestFBO()
{
	bool succeded=false;
	GLenum status=glCheckFramebufferStatus(GL_FRAMEBUFFER);
    switch(status)
    {
    case GL_FRAMEBUFFER_COMPLETE:
        succeded=true;
		break;
    case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
        alert("[ERROR] Framebuffer incomplete: Attachment is NOT complete.");
        break;
    case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
        alert("[ERROR] Framebuffer incomplete: No image is attached to FBO.");
        break;
    case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
        alert("[ERROR] Framebuffer incomplete: Draw buffer.");
        break;
    case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
        alert("[ERROR] Framebuffer incomplete: Read buffer.");
        break;
    case GL_FRAMEBUFFER_UNSUPPORTED:
        alert("[ERROR] Unsupported by FBO implementation.");
        break;
    default:
        alert("[ERROR] Unknow error.");
        break;
    }
	return succeded;
}
void FrameBuffer::Bind()
{
	globals.Graphics.Height=Height;
	globals.Graphics.Width=Width;
	glBindFramebuffer(GL_FRAMEBUFFER, FBOID);
}
void FrameBuffer::Unbind()
{
	globals.Graphics.ResetDimensions();
	Reset();
	glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
Vec2 FrameBuffer::GetDimensions()
{
	return Vec2(Width, Height);
}
void FrameBuffer::BindTexture(string name)
{
	Texture* texture=_TEXTURE(Attachments.GetByName(name));
	if(texture)
	{
		texture->Bind();
		if(texture->bGenerateMipmaps)
			glGenerateMipmap(GL_TEXTURE_2D);
	}
}
void FrameBuffer::UnbindTexture()
{
	glBindTexture(GL_TEXTURE_2D, 0);
}
void FrameBuffer::EnableColors(unsigned int nColor)
{
	if(nColor>0
		&&nColor<=GL_MAX_COLOR_ATTACHMENTS)
	{
		GLenum buffs[GL_MAX_COLOR_ATTACHMENTS];
		for(unsigned int i=0;i<nColor;i++)
			buffs[i]=GL_COLOR_ATTACHMENT0+i;
		glDrawBuffers(nColor, buffs);
	}
}
void FrameBuffer::DisableColors()
{
	glDrawBuffer(GL_NONE);
}
void FrameBuffer::AttachToColor(sptr<GameResource> texture_resc, unsigned int nColor, unsigned int nFace)
{
	Texture* texture=_TEXTURE(texture_resc);
	nColor=std::min(nColor, (unsigned int)GL_MAX_COLOR_ATTACHMENTS);
	if(texture)
	{
		if(texture->IsCubeMap())
			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+nColor, GL_TEXTURE_CUBE_MAP_POSITIVE_X+nFace, texture->GetID(), 0);
		else glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+nColor, GL_TEXTURE_2D, texture->GetID(), 0);
	}
	else glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+nColor, GL_TEXTURE_2D, 0, 0);
	TestFBO();
	Vec2 dimensions=texture->GetDimensions();
	glViewport(0,0,dimensions.x, dimensions.y);
}
void FrameBuffer::AttachToDepth(sptr<GameResource> texture_resc)
{
	Texture* texture=_TEXTURE(texture_resc);
	unsigned int id=0;
	if(texture)
		id=texture->GetID();
	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, id, 0);
	TestFBO();
}
sptr<GameResource> FrameBuffer::GetAttachment(string name)
{
	sptr<GameResource> resc=Attachments.GetByName(name);
	return resc;
}
sptr<GameResource> FrameBuffer::GetDepth()
{
	return GetAttachment("DEPTH");	
}
sptr<GameResource> FrameBuffer::GetColor(unsigned int attachmentID)
{
	string name="COLOR"<<string::intToAlpha(attachmentID);
	return GetAttachment(name);
}
void FrameBuffer::AddTexture(TextureDesc &desc, GLenum attachmentPoint)
{
	Texture* texture=new Texture(desc);
	Attachments.push_back(sptr<GameResource>(texture));
	glFramebufferTexture2D(GL_FRAMEBUFFER, attachmentPoint, GL_TEXTURE_2D, texture->GetID(), 0);
}
void FrameBuffer::AttachDepth()
{
	TextureDesc tdesc;
	tdesc.Name="DEPTH";
	tdesc.Width=Width;
	tdesc.Height=Height;
	tdesc.Format=GL_DEPTH_COMPONENT;
	tdesc.InternalFormat=GL_DEPTH_COMPONENT32;
	tdesc.Type=GL_FLOAT;
	AddTexture(tdesc, GL_DEPTH_ATTACHMENT);

}
void FrameBuffer::AttachColor(unsigned int nColors)
{
	TextureDesc tdesc;
	tdesc.Width=Width;
	tdesc.Height=Height;
	nColors=std::min(nColors, (unsigned int)GL_MAX_COLOR_ATTACHMENTS);
	for(unsigned int i=0;i<nColors;i++)
	{
		tdesc.Name="COLOR"<<string::intToAlpha(i);
		AddTexture(tdesc, GL_COLOR_ATTACHMENT0+i);
	}
	nColorAttachments=nColors;
}
void FrameBuffer::DetachDepth()
{
	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
}
void FrameBuffer::Reset()
{
	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
	if(bDepthEnabled)
		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
	EnableColors(1);
}
